package dao

import (
	"context"
	"errors"
	"forum/internal/common/slice"
	"forum/internal/domain"
	"forum/internal/domain/vo"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
	"time"
)

type ArticleDao interface {
	Insert(ctx context.Context, article domain.Article) (int64, error)
	Update(ctx context.Context, article domain.Article) error
	Sync(ctx context.Context, article domain.Article) (int64, error)
	GetAllByAuthorId(ctx context.Context, id int64, page vo.Page) ([]domain.Article, error)
	GetByAuthorId(ctx context.Context, id int64) (domain.Article, error)
	GetPubById(ctx context.Context, id int64) (domain.Article, error)
}

type ArticleSQLDao struct {
	db *gorm.DB
}

func NewArticleSQLDao(db *gorm.DB) *ArticleSQLDao {
	return &ArticleSQLDao{db: db}
}

func (a *ArticleSQLDao) Insert(ctx context.Context, article domain.Article) (int64, error) {
	now := time.Now().UnixMilli()
	article.Ctime = now
	article.Utime = now
	dao := a.EntityToDao(article)
	err := a.db.WithContext(ctx).Create(&dao).Error
	return dao.Id, err
}

func (a *ArticleSQLDao) Update(ctx context.Context, article domain.Article) error {
	now := time.Now().UnixMilli()
	dao := a.EntityToDao(article)
	res := a.db.WithContext(ctx).Model(&dao).
		Where("id = ? AND author_id = ?", dao.Id, dao.AuthorId).Updates(map[string]any{
		"title":   dao.Title,
		"content": dao.Content,
		"status":  dao.Status,
		"utime":   now,
	})
	if res.Error != nil {
		return res.Error
	}
	if res.RowsAffected == 0 {
		return errors.New("ID 不对或者创作者不对")
	}
	return nil
}

func (a *ArticleSQLDao) Sync(ctx context.Context, article domain.Article) (int64, error) {
	id := article.Id
	err := a.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		var (
			err error
		)
		artDao := NewArticleSQLDao(tx)
		if id > 0 {
			err = artDao.Update(ctx, article)
		} else {
			id, err = artDao.Insert(ctx, article)
		}
		if err != nil {
			return err
		}
		article.Id = id
		now := time.Now().UnixMilli()
		dao := a.EntityToDao(article)
		pubArt := PublishArticle(dao)
		pubArt.Ctime = now
		pubArt.Utime = now
		err = tx.Clauses(clause.OnConflict{
			Columns: []clause.Column{{Name: "id"}},
			DoUpdates: clause.Assignments(map[string]interface{}{
				"title":   pubArt.Title,
				"content": pubArt.Content,
				"utime":   now,
				"status":  pubArt.Status,
			}),
		}).Create(&pubArt).Error
		return err
	})
	return id, err
}

func (a *ArticleSQLDao) GetAllByAuthorId(ctx context.Context, id int64, page vo.Page) ([]domain.Article, error) {
	var arts []Article

	err := a.db.WithContext(ctx).
		Where("author_id = ?", id).
		Offset(page.Offset).Limit(page.Limit).
		// a ASC, B DESC
		Order("utime DESC").
		Find(&arts).Error
	articles := slice.Map[Article, domain.Article](arts, func(idx int, src Article) domain.Article {
		return a.DaoToEntity(src)
	})
	return articles, err
}

func (a *ArticleSQLDao) GetByAuthorId(ctx context.Context, id int64) (domain.Article, error) {
	var art Article
	err := a.db.WithContext(ctx).
		Where("id = ?", id).First(&art).Error
	return a.DaoToEntity(art), err
}

func (a *ArticleSQLDao) GetPubById(ctx context.Context, id int64) (domain.Article, error) {
	var res PublishArticle
	err := a.db.WithContext(ctx).
		Where("id = ?", id).
		First(&res).Error
	return a.DaoToEntity(Article(res)), err
}

func (a *ArticleSQLDao) EntityToDao(article domain.Article) Article {
	return Article{
		Id:       article.Id,
		Title:    article.Title,
		Content:  article.Content,
		Status:   article.Status.ToUnit8(),
		AuthorId: article.Author.Id,
		Ctime:    article.Ctime,
		Utime:    article.Utime,
	}
}
func (a *ArticleSQLDao) DaoToEntity(article Article) domain.Article {
	return domain.Article{
		Id:      article.Id,
		Title:   article.Title,
		Content: article.Content,
		Status:  domain.ArticleStatus(article.Status),
		Author: domain.Author{
			Id: article.AuthorId,
		},
		Ctime: article.Ctime,
		Utime: article.Utime,
	}
}

type PublishArticle Article

type Article struct {
	Id      int64  `gorm:"primaryKey,autoIncrement"`
	Title   string `gorm:"type=varchar(4096)"`
	Content string `gorm:"type=BLOB"`
	// 根据创作者ID来查询
	AuthorId int64 `gorm:"index"`
	Status   uint8
	Ctime    int64
	// 更新时间
	Utime int64
}
